﻿using NMF.Synchronizations;
using NMF.Expressions.Linq;
using NMF.Transformations;
using NMF.Transformations.Core;
using NMF.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TTC2015.JavaRefactoring.Java.Classifiers;
using TTC2015.JavaRefactoring.Java.Containers;
using TTC2015.JavaRefactoring.Java.Members;
using TTC2015.JavaRefactoring.TypeGraphBasic;
using TTC2015.JavaRefactoring.Java.Types;
using NMF.Collections.ObjectModel;

namespace TTC2015.JavaRefactoring
{
    public class JavaPGSynchronization : ReflectiveSynchronization
    {
        private static IClass GetBaseClass(ITypeReference typeReference)
        {
            var namespaceClassifier = typeReference as INamespaceClassifierReference;
            if (namespaceClassifier != null)
            {
                foreach (var reference in namespaceClassifier.ClassifierReferences)
                {
                    var baseClass = reference.Target as IClass;
                    if (baseClass != null) return baseClass;
                }
            }
            return null;
        }

        public class OwnClasses2TypeGraph : SynchronizationRule<ObservableList<IClass>, ITypeGraph>
        {
            public override void DeclareSynchronization()
            {
                SynchronizeMany(SyncRule<Class2Class>(), list => list, typeGraph => typeGraph.Classes);

                SynchronizeOpaque((classes, typeGraph, direction, context) =>
                {
                    if (direction == SynchronizationDirection.LeftToRight)
                    {
                        typeGraph.Methods.AddRange(context.Trace.FindAllIn(Rule<Method2Method>()));
                        typeGraph.Fields.AddRange(context.Trace.FindAllIn(Rule<Field2Field>()));
                    }
                });
            }
        }


        public class Class2Class : SynchronizationRule<IClass, ITClass>
        {
            public override void DeclareSynchronization()
            {
                Synchronize(cl => cl.Name, cl => cl.TName);

                SynchronizeMany(SyncRule<Member2MemberDefinition>(),
                    cl => cl.Members.Where(m => m is IClassMethod || m is IField),
                    cl => cl.Defines);

                Synchronize(this,
                    cl => GetBaseClass(cl.Extends), RegisterNewBaseClass,
                    cl => cl.ParentClass);
            }

            private void RegisterNewBaseClass(IClass @class, IClass newBaseClass)
            {
                var classifierReference = @class.Extends as IClassifierReference;
                if (newBaseClass == null)
                {
                    if (classifierReference != null)
                    {
                        @class.Extends = null;
                    }
                }
                else
                {
                    if (classifierReference == null)
                    {
                        classifierReference = new ClassifierReference();
                        classifierReference.Target = newBaseClass;
                        @class.Extends = classifierReference;
                    }
                    else
                    {
                        classifierReference.Target = newBaseClass;
                    }
                }
            }
        }

        public class Member2MemberDefinition : SynchronizationRule<IMember, ITMember>
        {
            public override void DeclareSynchronization() { }
        }

        public class Method2MethodDefinition : SynchronizationRule<IMethod, ITMethodDefinition>
        {
            public override bool ShouldCorrespond(IMethod left, ITMethodDefinition right, ISynchronizationContext context)
            {
                var sig = right.Signature;
                if (sig == null) return false;
                var meth = sig.Method;
                if (meth == null) return false;
                return left.Name == meth.TName;
            }

            public override void DeclareSynchronization()
            {
                MarkInstantiatingFor(SyncRule<Member2MemberDefinition>());

                Synchronize(meth => meth.Name, meth => meth.Signature.Method.TName);

                LeftToRight.Require(Rule<Method2MethodSignature>(), meth => meth.Name, 
                    meth => meth.Parameters.Select(p => GetBaseClass(p.TypeReference)).Where(p => p != null).AsItemEqual(),
                    (meth, signature) => meth.Signature = signature);
            }
        }

        public class Method2MethodSignature : TransformationRule<string, IEnumerable<IClass>, ITMethodSignature>
        {
            public override void Transform(string name, IEnumerable<IClass> paramList, ITMethodSignature output, ITransformationContext context)
            {
                var class2class = this.SyncRule<Class2Class>();
                foreach (var type in paramList)
                {
                    output.ParamList.Add(context.Trace.ResolveIn(class2class.LeftToRight, type));
                }
            }

            public override void RegisterDependencies()
            {
                Require(Rule<Method2Method>(), (name, type) => name, (signature, field) => signature.Method = field);
            }
        }

        public class Method2Method : TransformationRule<string, ITMethod>
        {
            public override void Transform(string input, ITMethod output, ITransformationContext context)
            {
                output.TName = input;
            }
        }

        public class Field2FieldDefinition : SynchronizationRule<IField, ITFieldDefinition>
        {
            public override bool ShouldCorrespond(IField left, ITFieldDefinition right, ISynchronizationContext context)
            {
                var sig = right.Signature;
                if (sig == null) return false;
                var field = sig.Field;
                if (field == null) return false;
                return field.TName == left.Name;
            }

            public override void DeclareSynchronization()
            {
                MarkInstantiatingFor(SyncRule<Member2MemberDefinition>());

                Synchronize(f => f.Name, f => f.Signature.Field.TName);

                LeftToRight.Require(Rule<Field2FieldSignature>(),
                    f => f.Name, f => GetBaseClass(f.TypeReference),
                    (field, signature) => field.Signature = signature);
            }
        }

        public class Field2FieldSignature : TransformationRule<string, IClass, ITFieldSignature>
        {
            public override void Transform(string name, IClass fieldType, ITFieldSignature output, ITransformationContext context)
            {
                var class2class = this.SyncRule<Class2Class>();
                if (fieldType != null)
                {
                    output.Type = context.Trace.ResolveIn(class2class.LeftToRight, fieldType);
                }
            }

            public override void RegisterDependencies()
            {
                Require(Rule<Field2Field>(), (name, type) => name, (signature, field) => signature.Field = field);
            }
        }

        public class Field2Field : TransformationRule<string, ITField>
        {
            public override void Transform(string input, ITField output, ITransformationContext context)
            {
                output.TName = input;
            }
        }

    }
}
